package com.jordi.tetris.model;

import java.util.ArrayList;

public class Board {

	private Matrix<Block> board;
	private ArrayList<Integer> colHeights;
	private Piece currentPiece;

	public Board() {
		this(TetrisConstants.DEFAULT_BOARD_ROWS,
				TetrisConstants.DEFAULT_BOARD_COLUMNS);
	}

	public Board(int rows, int columns) {

		board = new Matrix<Block>(rows, columns);
		colHeights = new ArrayList<Integer>(columns);

		for (int i = 0; i < columns; i++) {
			for (int j = 0; j < rows; j++) {
				board.set(BlockFactory.createEmptyBlock(), j, i);
			}
			colHeights.add(new Integer(rows));
		}
		updateColHeights();
		setNextPiece();
	}

	private void updateColHeights() {
		ArrayList<Block> column;
		for (int i = 0; i < board.getColumns(); i++) {
			column = board.getColumn(i);
			for (int j = 0; j < board.getRows(); j++) {
				if (!column.get(j).isEmpty()) {
					colHeights.set(i, new Integer(j));
					break;
				}
			}
		}
	}

	private void setNextPiece() {
		currentPiece = PieceFactory.createPiece();
		currentPiece.setPosition(new Position((board.getColumns() / 2) - (currentPiece.getWidth() / 2), 0));
	}

	public boolean canMovePieceDown() {
		for (int i = currentPiece.getX(); i < currentPiece.getX()
				+ currentPiece.getWidth(); i++) {
			if (!currentPiece.getBlock(currentPiece.getHeight() - 1,
					i - currentPiece.getX()).isEmpty()) {
				if (currentPiece.getY() + currentPiece.getHeight() + 1 > getHeightColumn(i))
					return false;
			}
		}
		return true;
	}

	public boolean canMovePieceLeft() {
		if (currentPiece.getX() == 0)
			return false;

		int heightLeftCol = getHeightColumn(currentPiece.getX() - 1);
		for (int i = currentPiece.getY(); i < currentPiece.getY()
				+ currentPiece.getHeight(); i++) {
			if (i > heightLeftCol)
				return false;
		}
		return true;
	}

	public boolean canMovePieceRight() {
		if (currentPiece.getX() + currentPiece.getWidth() == board.getColumns())
			return false;

		int heightRightCol = getHeightColumn(currentPiece.getX()
				+ currentPiece.getWidth());
		for (int i = currentPiece.getY(); i < currentPiece.getY()
				+ currentPiece.getHeight(); i++) {
			if (i > heightRightCol)
				return false;
		}
		return true;
	}

	public boolean isAnyColumnFull() {
		for (int i = 0; i < board.getColumns(); i++) {
			if (isColumnFull(i))
				return true;
		}
		return false;
	}

	private boolean isColumnFull(int column) {
		return colHeights.get(column) == 0;
	}

	public void downPiece() {
		downPiece(1);
	}

	public void leftPiece() {
		leftPiece(1);
	}

	public void rightPiece() {
		rightPiece(1);
	}

	private void downPiece(int num) {
		currentPiece.moveDown(num);
	}

	private void leftPiece(int num) {
		currentPiece.moveLeft(num);
	}

	private void rightPiece(int num) {
		currentPiece.moveRight(num);
	}

	public void mergePieceToBoard() {

		for (int i = 0; i < currentPiece.getHeight(); i++) {
			for (int j = 0; j < currentPiece.getWidth(); j++) {
				if (!currentPiece.getBlock(i, j).isEmpty())
					board.set(currentPiece.getBlock(i, j), currentPiece.getY()
							+ i, currentPiece.getX() + j);
			}
		}

		updateColHeights();
		setNextPiece();
	}

	public boolean isRowFull(int row) {
		ArrayList<Block> r = board.getRow(row);
		for (int i = 0; i < board.getColumns(); i++) {
			if (r.get(i).isEmpty())
				return false;
		}
		return true;
	}

	public int cleanFullRows() {
		int fullRows = 0;
		int i = board.getRows() - 1;
		int minHeight = getMinHeightColumn();
		while (i > minHeight) {
			if (isRowFull(i)) {
				fullRows++;
				board.fallRow(i - 1);
				cleanFirstRow();
			}
		}
		updateColHeights();
		return fullRows;
	}

	private void cleanFirstRow() {
		for (int i = 0; i < board.getColumns(); i++) {
			board.set(BlockFactory.createEmptyBlock(), 0, i);
		}
	}

	public int getHeightColumn(int column) {
		return colHeights.get(column).intValue();
	}

	public int getMinHeightColumn() {
		int min = 0;
		for (int i = 0; i < colHeights.size(); i++) {
			if (colHeights.get(i) > min)
				min = colHeights.get(i);
		}
		return min;
	}

	public Piece getCurrePiece() {
		return currentPiece;
	}

	public void rotatePiece() {
		currentPiece.rotate();
		updatePiecePosition();
	}

	private void updatePiecePosition() {
		int diff = currentPiece.getX() + currentPiece.getWidth()
				- board.getColumns();
		if (diff > 0) {
			leftPiece(diff);
		}
	}

	public Block getBlock(int row, int column) {
		return board.get(row, column);
	}

	public int getRows() {
		return board.getRows();
	}

	public int getColumns() {
		return board.getColumns();
	}

	@Override
	public String toString() {
		return "Board: " + board.toString();
	}

}
